#pragma once

#include "Sem.hpp"
#include "Mutex.hpp"
#include <vector>

static const int g_cap = 5;

using namespace SemModule;
using namespace MutexModule;

template <class T>
class RingQueue
{
public:
    RingQueue(int cap = g_cap)
        :_cap(cap)
        ,_rq(cap)
        ,_blank_sem(cap)
        ,_p_step(0)
        ,_data_sem(0)
        ,_c_step(0)
    {}

    ~RingQueue()
    {}

    void Equeue(const T& in)
    {
        //生产者
        //申请空位置的信号量，信号量值减1，代表空位数减少一个
        //先申请信号量，再去申请锁，类比于，先买票，再去去电影院排队访问
        _blank_sem.P();
        {
            // 使用生产者锁，保证对队列的访问是线程安全的
            LockGuard gurad(_pro_lock);
            //入队列
            _rq[_p_step] = in;
            //维护下标
            ++_p_step;
            _p_step %= _cap; // 保证索引循环
        }
        // 生产者生产数据后，唤醒消费者，数据信号量值加1
        _data_sem.V();
    }

    void Pop(T* out)
    {
        //消费者
        //申请数据的信号量，信号量值减1，代表数据数减少一个
        _data_sem.P();
        {
            LockGuard gurad(_con_lock);
            //从队列中取出数据
            *out = _rq[_c_step];
            //维护下标
            ++_c_step;
            _c_step %= _cap; // 保证索引循环
        }
        // 消费者消费一个数据后，唤醒生产者，空位信号量值加1
        _blank_sem.V();
    }


private:
std::vector<T> _rq;  // 存储数据的环形队列
int _cap;            // 队列的最大容量

// 信号量：管理空位置和数据的同步
Sem _blank_sem; // 空位置的信号量，表示可以插入数据的位置
int _p_step;    // 生产者索引，指向下一个插入的位置

Sem _data_sem;  // 数据信号量，表示可以被消费的数据
int _c_step;    // 消费者索引，指向下一个待消费的位置

// 互斥锁：保护生产者和消费者的操作
Mutex _con_lock;  // 消费者锁，保证消费者线程安全
Mutex _pro_lock;  // 生产者锁，保证生产者线程安全
};

